
lista_risorse* calcola_possibili_risorse(carta* risorse_campo, lista_risorse* risorse_ereditate, int solo_commerciabili) {
	if (risorse_campo == NULL) {
		if (risorse_ereditate == NULL) {
			risorse_ereditate = malloc(sizeof(lista_risorse));
			assert(risorse_ereditate != NULL);
			risorse_ereditate->succ = NULL;
			azzera_risorse(risorse_ereditate->valore);
		}
		return risorse_ereditate;
	}
	
	if (solo_commerciabili && !risorse_campo->commerciabile) {
		return calcola_possibili_risorse(risorse_campo->succ, risorse_ereditate, solo_commerciabili);
	}
	else
	{
		if (risorse_ereditate == NULL) {
			risorse_ereditate = malloc(sizeof(lista_risorse));
			assert(risorse_ereditate != NULL);
			risorse_ereditate->succ = NULL;
			azzera_risorse(risorse_ereditate->valore);
		}
		if (risorse_campo->multipla) {
			// moltiplica le possibilità
			lista_risorse *puntatore = NULL, *puntatore_new;
			int i;
			for (i = 0; i < 8; i++){
				if(risorse_campo->produzione[i] > 0){
					puntatore_new = lista_risorse_duplica(risorse_ereditate);
					lista_risorse_aggiungi_ciascuno(risorse_campo, puntatore_new, i);			
					puntatore = lista_risorse_append(puntatore, puntatore_new);
				}
			}
			dealloca_lista_risorse(risorse_ereditate);
			return calcola_possibili_risorse(risorse_campo->succ, puntatore, solo_commerciabili);
		} else {
			// aggiungi la risorsa singola
			lista_risorse_aggiungi_ciascuno(risorse_campo, risorse_ereditate, -1);
			return calcola_possibili_risorse(risorse_campo->succ, risorse_ereditate, solo_commerciabili);
		}
	}
}

modi* modi_aggiungi_in_fondo(modi* output, modi* next){
	if(output == NULL) return next;
	if(output->succ == NULL){
		output->succ = next;
		return output;
	}
	output = modi_aggiungi_in_fondo(output->succ, next);
	return output;
}

/*
	Date le risorse prodotte da self, dx, sx, il giocatore e la carta da_giocare
	restituisce una lista di tutti i modi validi di dare le monete a dx e a sx
*/
modi* modi_possibili_acquisti(carta* da_giocare, costo_acquisto* costo_acquisto_risorse, lista_risorse* p_self, lista_risorse* p_sx, lista_risorse* p_dx, Modo_gioca_carta azione) {
	modi *possibili_modi = NULL;
	int i, mancante, a_dx, a_sx;
	modi *tmp = NULL, *iter = NULL, *new_modi = NULL;
	
	possibili_modi = malloc(sizeof(modi));
	assert(possibili_modi != NULL);
	possibili_modi->azione = azione;
	possibili_modi->paga.sinistra = 0;
	possibili_modi->paga.destra = 0;
	possibili_modi->paga.banco = 0;
	possibili_modi->succ = NULL;
	
	// per ogni risorsa
	// INVARIANTE: possibili_modi è la lista di modi di acquistare le risorse [0..i-1]
	for (i = 0; i < 7; i++) {
		if (p_self->valore[i] + p_sx->valore[i] + p_dx->valore[i] < da_giocare->costo[i]) {
			dealloca_modi(possibili_modi);
			return NULL;
		}
		
		mancante = da_giocare->costo[i] - p_self->valore[i];
		if (mancante <= 0) continue;
		
		new_modi = NULL;
		
		// per ogni possibile distribuzione delle risorse da acquistare
		// PRE: possibili_modi è la lista di modi di acquistare le risorse [0..i-1]
		// INVARIANTE: new_modi è la lista di modi di acquistare le risorse [0..i]
		//             chiedendo al max a_sx-1 risorse di tipo i a quello a sinistra
		for (a_sx = 0; a_sx <= mancante; a_sx++) {
			if (a_sx < 0 || a_sx > p_sx->valore[i]) continue;
			a_dx = mancante-a_sx;
			if (a_dx < 0 || a_dx > p_dx->valore[i]) continue;
			
			// fai una copia dei modi di acquistare le risorse [0..i-1]
			// e aggiungiungici il caso (a_sx, a_dx)
			// poi mettila in fondo a new_modi
			
			printf("Si può comprare %d risorse di tipo %d a sx e %d a dx\n", a_sx, i, a_dx);
			
			tmp = modi_copia(possibili_modi);
			
			// aggiungi a ciascuno
			iter = tmp;
			while (iter != NULL) {
				if (i == LEGNO || i == PIETRA || i == ARGILLA || i == ORO) {
					iter->paga.sinistra += a_sx * costo_acquisto_risorse->normali_sinistra;
					iter->paga.destra += a_dx * costo_acquisto_risorse->normali_destra;
				} else
				if (i == STOFFA || i == VETRO || i == PAPIRI) {
					iter->paga.sinistra += a_sx * costo_acquisto_risorse->speciali_sinistra;
					iter->paga.destra += a_dx * costo_acquisto_risorse->speciali_destra;
				}
				iter = iter->succ;
			}
			
			new_modi = modi_append(new_modi, tmp);
		}
		
		dealloca_modi(possibili_modi);
		possibili_modi = new_modi;
	}
	return possibili_modi;
}

modi* modi_togli_costo_maggiore(modi* lista_modi, int soldi) {
	if (lista_modi == NULL) return NULL;
	
	if (lista_modi->paga.destra + lista_modi->paga.sinistra + lista_modi->paga.banco > soldi) {
		modi *succ = lista_modi->succ;
		free(lista_modi);
		return modi_togli_costo_maggiore(succ, soldi);
	} else {
		lista_modi->succ = modi_togli_costo_maggiore(lista_modi->succ, soldi);
		return lista_modi;
	}
}

/*
	Se c'è un modo di comprarla gratis, restituisce solo quello
	Se non basta, calcola tutti i possibili acquisti dai vicini e ne restituisce la lista
*/
modi* calcola_modi_di_comprare(partita *game, int num_giocatore, carta *da_giocare, Modo_gioca_carta modo_di_giocare) {
	modi *output = NULL;
	if (modo_di_giocare == VENDI) {
		output = malloc(sizeof(modi));
		assert(output != NULL);
		output->azione = VENDI;
		output->paga.destra = 0;
		output->paga.sinistra = 0;
		output->paga.banco = 0;
		output->succ = NULL;
		return output;
	} else
	if (modo_di_giocare == MERAVIGLIA || modo_di_giocare == GIOCA) {
		if (modo_di_giocare == MERAVIGLIA) {
			da_giocare = game->giocatori[num_giocatore].meraviglia->fase;
			if (da_giocare == NULL) {
				// hai già costruito tutte le meraviglie
				return NULL;
			}
		}
		
		// Conotrolla se si può giocare subito gratis grazie alla cetena di dipendenze
		if (da_giocare->gioca_gratis) {
			if (lista_carte_cerca_per_nome(da_giocare->gioca_gratis, game->giocatori[num_giocatore].strutture_campo)
				|| lista_carte_cerca_per_nome(da_giocare->gioca_gratis, game->giocatori[num_giocatore].risorse_campo)) {
				output = malloc(sizeof(modi));
				assert(output != NULL);
				output->azione = modo_di_giocare;
				output->paga.destra = 0;
				output->paga.sinistra = 0;
				output->paga.banco = 0;
				output->succ = NULL;
				return output;
			}
		}
		
		if (da_giocare->costo[MONETE] > 0) {
			if (game->giocatori[num_giocatore].soldi < da_giocare->costo[MONETE]) {
				// costa troppo
				return NULL;
			} else {
				// in 7 wonders non capita mai che il costo sia in monete e anche in materiali
				output = malloc(sizeof(modi));
				assert(output != NULL);
				output->azione = modo_di_giocare;
				output->paga.destra = 0;
				output->paga.sinistra = 0;
				output->paga.banco = da_giocare->costo[MONETE];
				output->succ = NULL;
				return output;
			}
		}
		
		// d'ora in poi assumiamo che il costo sia solo in materiali
		lista_risorse *proprie;
		proprie = calcola_possibili_risorse(game->giocatori[num_giocatore].risorse_campo, NULL, NON_SOLO_COMMERCIABILI);
		proprie = lista_risorse_elimina_doppioni(proprie);
		printf("produzione propria:\n"); lista_risorse_stampa(proprie);
		
		if (lista_risorse_permette_di_giocare(proprie, da_giocare)) {
			// in qualche modo si produce già tutto il necessario
			dealloca_lista_risorse(proprie);
			output = malloc(sizeof(modi));
			assert(output != NULL);
			output->azione = modo_di_giocare;
			output->paga.destra = 0;
			output->paga.sinistra = 0;
			output->paga.banco = 0;
			output->succ = NULL;
			return output;
		}
		
		// bisogna controllare se i vicini producono quello che manca
		lista_risorse *produce_dx, *produce_sx;
		produce_dx = calcola_possibili_risorse(game->giocatori[num_giocatore].destra->risorse_campo, NULL, SOLO_COMMERCIABILI);
		produce_dx = lista_risorse_elimina_doppioni(produce_dx);
		produce_sx = calcola_possibili_risorse(game->giocatori[num_giocatore].sinistra->risorse_campo, NULL, SOLO_COMMERCIABILI);
		produce_sx = lista_risorse_elimina_doppioni(produce_sx);
		printf("produce_dx:\n"); lista_risorse_stampa(produce_dx);
		printf("produce_sx:\n"); lista_risorse_stampa(produce_sx);
		
		// per ogni possibile combinazione
		lista_risorse *p_self, *p_sx, *p_dx;
		modi *nuovi_modi;
		printf("modi_possibili_acquisti:\n");
		for (p_self = proprie; p_self != NULL; p_self = p_self->succ) {
			for (p_sx = produce_sx; p_sx != NULL; p_sx = p_sx->succ) {
				for (p_dx = produce_dx; p_dx != NULL; p_dx = p_dx->succ) {
					nuovi_modi = modi_possibili_acquisti(da_giocare, &game->giocatori[num_giocatore].costo_acquisto_risorse, p_self, p_sx, p_dx, modo_di_giocare);
					nuovi_modi = modi_togli_costo_maggiore(nuovi_modi, game->giocatori[num_giocatore].soldi);
					output = modi_append(output, nuovi_modi);
				}
			}
		}
		output = modi_elimina_doppioni(output);
		
		dealloca_lista_risorse(proprie);
		dealloca_lista_risorse(produce_sx);
		dealloca_lista_risorse(produce_dx);
		return output;
	}
	return NULL;
}

modi* modi_togli_tutti(modi* modo, Modo_gioca_carta scelta){
	if(modo == NULL) return NULL;
	if(modo->azione != scelta){
		modi* next = modo->succ;
		modo->succ = NULL;
		free(modo);
		return modi_togli_tutti(next, scelta);
	}
	else{
		modo->succ = modi_togli_tutti(modo->succ, scelta);
		return modo;
	}
}

void gioca_carta(partita* game, giocatore* player, carta* card, modi* modo){	//per giocare la carta si basa unicamente su "modi", già tolta dalla mano
	assert(game != NULL && player != NULL && modo != NULL);
	carta *fase;
	switch(modo->azione){
		case VENDI:
			player->soldi += 3;
			game->scarti = lista_carte_aggiungi(game->scarti, card);
			break;

		case MERAVIGLIA:
			fase = player->meraviglia->fase;
			player->meraviglia->fase = lista_carte_togli(player->meraviglia->fase, fase);
			player->meraviglia->livello += 1;
			player->scarti_meraviglia = lista_carte_aggiungi(player->scarti_meraviglia, card);
			card = fase;
			// senza break
		case GIOCA:
			player->soldi -= modo->paga.sinistra;
			player->soldi -= modo->paga.destra;
			player->soldi -= modo->paga.banco;
			player->sinistra->soldi += modo->paga.sinistra;
			player->destra->soldi += modo->paga.destra;
			
			if (card->risorsa) {
				player->risorse_campo = lista_carte_aggiungi(player->risorse_campo, card);
			} else {
				player->strutture_campo = lista_carte_aggiungi(player->strutture_campo, card);
			}
			card->azione_istantanea(player);
			break;
	}
	return;
}

modi* modi_copia(modi* da_copiare){
	if (da_copiare == NULL) return NULL;
	modi *nuova_lista = malloc(sizeof(modi));
	assert(nuova_lista != NULL);	
	nuova_lista->azione = da_copiare->azione;
	nuova_lista->paga = da_copiare->paga;
	nuova_lista->succ = modi_copia(da_copiare->succ);
	return nuova_lista;	
}

modi* modi_append(modi* base, modi* alla_fine) {
	if (base == NULL) return alla_fine;
	base->succ = modi_append(base->succ, alla_fine);
	return base;	
}

modi* modi_togli(modi* lista_modi, modi* da_togliere){
	if(lista_modi == NULL) return NULL;
	if(lista_modi == da_togliere) {
		modi* new_da_togliere = da_togliere->succ;
		da_togliere->succ = NULL;
		return new_da_togliere;
	}
	lista_modi->succ = modi_togli(lista_modi->succ, da_togliere);
	return lista_modi;
}

modi* modi_cerca_gratis(modi* lista_modi){
	if(lista_modi == NULL) return NULL;
	if (lista_modi->paga.destra == 0 && lista_modi->paga.sinistra == 0 && lista_modi->paga.banco == 0) return lista_modi;
	return modi_cerca_gratis(lista_modi->succ);	
}

modi* modi_elimina_per_valore(modi* lista_modi, modi* da_togliere) {
	assert(da_togliere != NULL);
	if (lista_modi == NULL) return NULL;
	if (lista_modi->azione == da_togliere->azione
	      && lista_modi->paga.sinistra == da_togliere->paga.sinistra
		  && lista_modi->paga.destra == da_togliere->paga.destra
		  && lista_modi->paga.banco == da_togliere->paga.banco) {
		modi *succ = lista_modi->succ;
		free(lista_modi);
		return modi_elimina_per_valore(succ, da_togliere);
	}
	lista_modi->succ = modi_elimina_per_valore(lista_modi->succ, da_togliere);
	return lista_modi;
}

modi* modi_elimina_doppioni(modi* lista_modi) {
	if (lista_modi == NULL) return NULL;
	lista_modi->succ = modi_elimina_per_valore(lista_modi->succ, lista_modi);
	lista_modi->succ = modi_elimina_doppioni(lista_modi->succ);
	return lista_modi;
}

void dealloca_modi(modi* modo){
	if(modo == NULL) return;
	dealloca_modi(modo->succ);
	free(modo);
	return;
}
